#ifndef _DVMH_UTIL_H_
#define _DVMH_UTIL_H_

#include <stdlib.h>
#include <stdio.h>

typedef long Interval[2]; // (Начало,Конец) включительно
typedef long LoopBounds[3]; // (Начало,Конец,Шаг) ключительно

#ifndef INT_INDEX
typedef long IndexType;
#else
typedef int IndexType;
#endif

#ifndef CUDA_INT_OFFSET
typedef long CudaOffsetType;
#else
typedef int CudaOffsetType;
#endif

typedef long DvmhRegionRef, DvmhLoopRef, DvmhShadowRef, CudaOffsetTypeRef;

#define min(a, b) ((a)<(b)?(a):(b))
#define max(a, b) ((a)>(b)?(a):(b))

void performCopyCuda(int rank, int typeSize, int device1, long *header1, int device2, long *header2, Interval *cutting);

// Logging level
enum LogLevel {FATAL = 0, ERROR = 1, WARNING = 2, INFO = 3, DEBUG = 4, TRACE = 5, LOG_LEVELS};
extern enum LogLevel logLevel;
// Logging function
int dvmh_log_ex(enum LogLevel level, const char *fileName, int lineNumber, const char *fmt, ...);
// Shorthand for logging function
#define dvmh_log(level, ...) do { if ((level) <= logLevel) dvmh_log_ex(level, __FILE__, __LINE__, __VA_ARGS__); } while (0)
#define custom_log(level, func, ...) do { if ((level) <= logLevel) func(level, __FILE__, __LINE__, __VA_ARGS__); } while (0)

extern unsigned long DVM_LINE[32];

#ifdef NDEBUG
    #define eassert(expr) (void)(expr)
    #define assertCuda(expr) (void)(expr)
#else
    #define eassert assert
    #define assertCuda(expr) do { \
        cudaError_t err = (expr); \
        if (err != cudaSuccess) { \
            dvmh_log(FATAL, "CUDA NO SUCCESS! Error #%d - \"%s\"", err, cudaGetErrorString(err)); \
            dvmh_log(FATAL, "fdv line: %ld", DVM_LINE[0]); \
            exit(1); \
        } \
    } while (0)
#endif

static inline void safe_free(void **pp) {
    if (*pp) {
        free(*pp);
        *pp = 0;
    }
}

typedef struct {
    Interval **pieces;
    int piecesCount;
} DvmhPieces;

DvmhPieces *piecesNew(int size, int rank);
//DvmhPieces *piecesDup(int rank, DvmhPieces *p);
void piecesDelete(DvmhPieces *p);
//void piecesAppend(int rank, DvmhPieces *p1, DvmhPieces *p2);
void piecesAppendOne(int rank, DvmhPieces *p, Interval *inter);
//DvmhPieces *piecesSubtractOneOne(int rank, Interval *inter1, Interval *inter2);
//void piecesSubtractOne(int rank, DvmhPieces *p, Interval *inter);
DvmhPieces *piecesSubtract(int rank, DvmhPieces *pieces1, DvmhPieces *pieces2);
DvmhPieces *piecesIntersect(int rank, DvmhPieces *p1, DvmhPieces *p2);
void piecesUniteOne(int rank, DvmhPieces *p, Interval *inter);
void piecesOutOne(int level, const char *filename, int lineNumber, int rank, Interval *inter);
void piecesOut(int level, const char *filename, int lineNumber, int rank, DvmhPieces *p);

typedef struct {
    struct {
        void *key;
        void *value;
    } *records;
    int recordsCount;
} Dictionary;

Dictionary *dictNew();
void *dictFind(Dictionary *dict, void *key);
int dictErase(Dictionary *dict, void *key);
int dictAdd(Dictionary *dict, void *key, void *value);
void dictSet(Dictionary *dict, void *key, void *value);
void dictForEach(Dictionary *dict, void (*f)(), int numArgs, ...);
void dictDelete(Dictionary *dict);

void fillHeader(int rank, int typeSize, void *devAddr, Interval *portion, long *header);
void fillRealBlock(int rank, long *lowIndex, long *highIndex, Interval *havePortion, Interval *realBlock);

int ilog(long value);
int gcd(int a, int b);
void executeFunction(void (*f)(), void **params, int paramsCount);

#endif
